#pragma once

#include <type_traits>

namespace zeroplus::mq
{

    namespace Traits
    {

        // empty type
        struct EmptyType
        {
        };

        // expect false
        template <typename T>
        struct ExpectFalse : std::false_type
        {
        };

        // operatorable
        template <typename OT, typename T, typename = void>
        struct Operatorable : ExpectFalse<OT>
        {
        };

        template <typename OT, typename T>
        struct Operatorable<OT, T,
                            std::void_t<decltype(std::declval<OT &>() >> std::declval<T>()),
                                        decltype(std::declval<OT &>() << std::declval<T>())>> : std::true_type
        {
        };

        // member check
        template <typename T, typename F>
        constexpr auto has_member(F &&f) -> decltype(f(std::declval<T>()), true)
        {
            return true;
        }

        template <typename>
        constexpr auto has_member(...)
        {
            return false;
        }

    } // namespace Traits

} // namespace zeroplus::mq

// SFINAE原则（Substitution Failure Is Not An Error，替换失败不是错误）
#define MQ_HAS_MEMBER(T, member) zeroplus::mq::Traits::has_member<T>([](auto &&obj) -> decltype(obj.member) { return 0; })
